/* 
 * File:   main.cpp
 * Author: notes_17
 *
 * Created on 23. kv?ten 2009, 10:46
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

/*
 * 
 */
int sleepTime = 100;
int runningThreads = 0;

pthread_mutex_t cpu = PTHREAD_MUTEX_INITIALIZER;

typedef struct instruction {
    int idInstruction;
    int cycleCount;
    instruction* nextInstruction;
} INSTRUCTION;

typedef struct row {
    int idRow;
    int priority;
    INSTRUCTION* nextInstruction;
    row* nextRow;
} ROW;

typedef struct threadData {
    ROW* instrBlock;
    int id;
} THREADDATA;

ROW* data;

void cpuCycle(int id) {
    printf("%d --> cpu cycle begin\n", id);
    timespec* ts = new timespec();
    ts->tv_sec = 0;
    ts->tv_nsec = 1000000 * sleepTime;
    nanosleep(ts, ts);
    printf("%d --> cpu cycle end\n", id);
}

void ioCycle(int id) {
    printf("%d --> io cycle begin\n", id);
    timespec* ts = new timespec();
    ts->tv_sec = 0;
    ts->tv_nsec = 1000000 * sleepTime;
    nanosleep(ts, ts);
    printf("%d --> io cycle end\n", id);
}

void sleep(int id, int count) {
    printf("%d --> sleep for %d cycles begin\n", id, count);
    timespec* ts = new timespec();
    ts->tv_sec = 0;
    ts->tv_nsec = 1000000 * sleepTime * count;
    nanosleep(ts, ts);
    printf("%d --> sleep for %d cycles end\n", id, count);
}

void create(int id, int rowId) {
    printf("%d --> create new thread for instruction block %d\n", id, rowId);
}

void* run(void * args) {
    int cyclesSpent = 0;
    THREADDATA* param = (THREADDATA*) args;
    INSTRUCTION* myInstr = param->instrBlock->nextInstruction;
    int priority = param->instrBlock->priority;
    while (myInstr != NULL) {
        switch (myInstr->idInstruction) {
            case 1: printf("%d --> cpu for %d cycles\n", param->id, myInstr->cycleCount);
                for (int i = 0; i < myInstr->cycleCount; i++) {
                    cpuCycle(param->id);
                }
                break;
            case 2: printf("%d --> io for %d cycles\n", param->id, myInstr->cycleCount);
                for (int i = 0; i < myInstr->cycleCount; i++) {
                    ioCycle(param->id);
                }
                break;
            case 3: printf("%d --> sleep for %d cycles\n", param->id, myInstr->cycleCount);
                sleep(param->id, myInstr->cycleCount);
                break;
            case 4: printf("%d --> create for %d cycles\n", param->id, myInstr->cycleCount);
                create(param->id, myInstr->cycleCount);
                break;
            default: printf("%d --> Unknown instruction type!", param->id);
        }
        myInstr = myInstr->nextInstruction;
    }

//    for (int i = 0; i < 30; i++) {
//        printf("%d --> bezim..\n", param->id);
//        timespec* ts = new timespec();
//        ts->tv_sec = 0;
//        ts->tv_nsec = 1000000 * sleepTime;
//        nanosleep(ts, ts);
//    }
    printf("%d --> koncim\n", param->id);
    runningThreads--;
}

int create(void* args) {
    pthread_t id;
    pthread_create(&id, NULL, run, args);
    runningThreads++;
    return (int) id;
}

void init() {
    
    INSTRUCTION* lastInstr;
    INSTRUCTION* currInstr = new INSTRUCTION();
    currInstr->idInstruction = 1;
    currInstr->cycleCount = 5;
    currInstr->nextInstruction = NULL;

    ROW* currRow = new ROW();
    currRow->idRow = 1;
    currRow->priority = 1;
    currRow->nextInstruction = currInstr;
    lastInstr = currInstr;

    currInstr = new INSTRUCTION();
    currInstr->idInstruction = 2;
    currInstr->cycleCount = 10;
    currInstr->nextInstruction = NULL;

    lastInstr->nextInstruction = currInstr;

    data = currRow;

    // row 2
    currInstr = new INSTRUCTION();
    currInstr->idInstruction = 1;
    currInstr->cycleCount = 3;
    currInstr->nextInstruction = NULL;

    currRow = new ROW();
    currRow->idRow = 2;
    currRow->priority = 2;
    currRow->nextInstruction = currInstr;
    lastInstr = currInstr;

    currInstr = new INSTRUCTION();
    currInstr->idInstruction = 2;
    currInstr->cycleCount = 6;
    currInstr->nextInstruction = NULL;
    lastInstr->nextInstruction = currInstr;

    data->nextRow = currRow;
}

void print() {
    ROW* currRow = data;
    INSTRUCTION* currInstr;
    while(currRow != NULL) {
        printf("radek=%d, priorita=%d", currRow->idRow, currRow->priority);
        currInstr = currRow->nextInstruction;
        while(currInstr !=NULL) {
            printf(", instrId=%d, cyklu=%d", currInstr->idInstruction, currInstr->cycleCount);
            currInstr = currInstr->nextInstruction;
        }
        printf("\n");
        currRow = currRow->nextRow;
    }
}

int main(int argc, char** argv) {

    init();
    print();
    
    THREADDATA* t1 = new THREADDATA();
    t1->id = 1;
    ROW* row = data;
    int rowId = 1;
    while(row != NULL && row->idRow != rowId) {
        row = row->nextRow;
    }
    if (row == NULL) {
        printf("No row with id %d", rowId);
    } else {
        t1->instrBlock = row;
        int id = create((void*) t1);
        printf("Vytvoreno vlakno %d s id=%d\n", id, t1->id);
    }

    THREADDATA* t2 = new THREADDATA();
    t2->id = 2;
    rowId = 2;
    while(row != NULL && row->idRow != rowId) {
        row = row->nextRow;
    }
    if (row == NULL) {
        printf("No row with id %d", rowId);
    } else {
        t2->instrBlock = row;
    int id = create((void*)t2);
    printf("Vytvoreno vlakno s id=%d\n", id);
    }

    while (runningThreads > 0) {
        timespec* ts = new timespec();
        ts->tv_sec = 0;
        ts->tv_nsec = 1000000 * sleepTime;
        nanosleep(ts, ts);
    }

    return (EXIT_SUCCESS);
}

